package com.bms.service.impl;

import com.bms.common.request.ExpenseReq;
import com.bms.common.response.Result;
import com.bms.entity.Activity;
import com.bms.mapper.ActivityMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import com.bms.common.response.ExpenseResponse;
import com.bms.mapper.ExpenseManager;
import com.bms.service.ExpenseService;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Description:
 * Author: jingcaiqi1225@gmail.com
 * Date: 2024/11/4 16:28
 */
@Service
@Slf4j
public class ExpenseServiceImpl implements ExpenseService {

    @Autowired
    private ExpenseManager expenseManager;

    @Autowired
    private ActivityMapper activityMapper;

    @Override
    public int getSearchCount(Map<String, Object> params) {
        return expenseManager.getSearchCount(params);
    }

    /**
     * 根据页面参数搜索活动并返回相应的费用响应列表
     * 此方法覆盖自某个父类或接口，用于实现特定的搜索逻辑
     *
     * @param params 包含搜索条件和分页信息的参数映射，例如关键词、页码和每页大小等
     * @return 返回一个 ExpenseResponse 对象列表，每个对象包含一个页面的搜索结果信息
     */
    @Override
    public List<ExpenseResponse> searchActivitiesByPage(Map<String, Object> params) {
        return expenseManager.searchActivitiesByPage(params);
    }

    /**
     * 解析Excel文件并导入费用请求数据
     *
     * @param inputStream Excel文件的输入流
     * @param type 数据类型，1代表个人，2代表机构
     * @param budgetType 预算类型，1代表预配置金额，2代表配置总金额
     * @return 导入结果，包括成功或错误信息
     * @throws IOException 如果读取Excel文件时发生IO错误
     */
    @Override
    public Result parseExcel(InputStream inputStream, int type, int budgetType) throws IOException {
        List<ExpenseReq> list = new ArrayList<>();
        Workbook workbook = new XSSFWorkbook(inputStream);
        Sheet sheet = workbook.getSheetAt(0);
        Iterator<Row> rows = sheet.iterator();

        // 跳过注释行
        rows.next();

        if (!rows.hasNext()) {
            log.warn("Excel 文件为空，没有数据行。");
            return Result.error("Excel 文件为空，没有数据行。");
        }

        // 读取第一行完整数据
        Row firstRow = rows.next();
        ExpenseReq req = new ExpenseReq();
        req.setActivityId(getCellValue(firstRow.getCell(0)));
        req.setCategoryId(getCellValue(firstRow.getCell(4)));

        // 根据数据类型读取机构代码和人员代码
        if (type == 1) {
            String valueJ = getCellValue(firstRow.getCell(7)); // 机构代码
            if(valueJ != null || !valueJ.trim().isEmpty()) {
                req.setCompanyCode(valueJ);
            } else {
                return Result.error("机构代码有空数据行，请重新校验明细数据！");
            }

            String valueR = getCellValue(firstRow.getCell(8)); // 人员代码
            if(valueR != null || !valueR.trim().isEmpty()) {
                req.setPersonnelCode(valueR);
            } else {
                return Result.error("人员代码有空数据行，请重新校验明细数据！");
            }
        } else {
            String valueJ = getCellValue(firstRow.getCell(7)); // 机构代码
            if(valueJ != null || !valueJ.trim().isEmpty()) {
                req.setCompanyCode(valueJ);
            } else {
                return Result.error("机构代码有空数据行，请重新校验明细数据！");
            }

            String valueR = getCellValue(firstRow.getCell(8)); // 人员代码
            if(valueR == null || valueR.trim().isEmpty()) {
                req.setPersonnelCode(null);
            } else {
                return Result.error("人员代码有数据行，请重新校验明细数据！");
            }
        }

        req.setBudget(BigDecimal.valueOf(getCellNumericValue(firstRow.getCell(6))));
        req.setBudgetType(budgetType); // 预算类型  1 预配置金额 2 配置总金额
        req.setType(type); // 数据类型  1 个人 2 机构

        list.add(req);

        // 读取后续行的部分数据
        while (rows.hasNext()) {
            Row currentRow = rows.next();
            ExpenseReq importReq = new ExpenseReq();

            // 填充缺失的字段
            importReq.setActivityId(req.getActivityId());
            importReq.setCategoryId(req.getCategoryId());

            // 根据数据类型读取机构代码和人员代码
            if (type == 1) {
                String valueJ = getCellValue(currentRow.getCell(7)); // 机构代码
                if (valueJ != null || !valueJ.trim().isEmpty()) {
                    importReq.setCompanyCode(valueJ);
                } else {
                    return Result.error("机构代码有空数据行，请重新校验明细数据！");
                }

                String valueR = getCellValue(currentRow.getCell(8)); // 人员代码
                if (valueR != null || !valueR.trim().isEmpty()) {
                    importReq.setPersonnelCode(valueR);
                } else {
                    return Result.error("人员代码有空数据行，请重新校验明细数据！");
                }
            } else {
                String valueJ = getCellValue(currentRow.getCell(7)); // 机构代码
                if (valueJ != null || !valueJ.trim().isEmpty()) {
                    importReq.setCompanyCode(valueJ);
                } else {
                    return Result.error("机构代码有空数据行，请重新校验明细数据！");
                }

                String valueR = getCellValue(currentRow.getCell(8)); // 人员代码
                if (valueR == null || valueR.trim().isEmpty()) {
                    importReq.setPersonnelCode(null);
                } else {
                    return Result.error("人员代码有数据行，请重新校验明细数据！");
                }
            }

            importReq.setBudget(BigDecimal.valueOf(getCellNumericValue(currentRow.getCell(6)))); // 金额
            importReq.setBudgetType(budgetType); // 预算类型
            importReq.setType(type); // 数据类型

            list.add(importReq);
        }

        // 开始处理逻辑

        // 循环统计表中的金额总数
        BigDecimal totalBudget = BigDecimal.ZERO;
        for (ExpenseReq ex : list) {
            totalBudget = totalBudget.add(ex.getBudget());
            if (ex.getCompanyCode() == null) {
                return Result.error("机构代码有空数据行，请重新校验明细数据！");
            }

            if (ex.getPersonnelCode() == null && type == 1) {
                return Result.error("人员代码有空数据行，请重新校验明细数据！");
            }
        }

        // 根据活动id获取活动对对象
        Activity activity = activityMapper.selectByActivityId(req.getActivityId());

        // 预配置金额
        if (budgetType == 1) {
            if (totalBudget.compareTo(activity.getForecastAmount()) > 0) {
                return Result.error("预配置金额超出活动预发金额，请重新校验明细数据！");
            }
        }
        else { // 配置总金额
            if (totalBudget.compareTo(activity.getActivityBudget()) > 0) {
                return Result.error("配置总金额超出活动总金额，请重新校验明细数据！");
            }
        }

        // 添加活动明细表
        expenseManager.addExpense(list);
        return Result.success();
    }

    /**
     * 获取所有费用总览列表
     *
     * 此方法用于从expenseManager中获取所有已记录费用的总览信息列表
     * 它提供了一个概览，帮助用户理解总体的费用支出情况
     *
     * @return 包含所有费用总览信息的列表
     */
    @Override
    public List<ExpenseResponse> getExpenseTotalList() {
        return expenseManager.getExpenseTotalList();
    }

    /**
     * 获取条线明细列表
     *
     * 该方法用于获取按条线汇总的费用支出明细列表
     * 它有助于分析不同条线的费用支出情况
     *
     * @return 包含线路费用摘要的列表
     */
    @Override
    public List<ExpenseResponse> getLineSummaryList() {
        return expenseManager.getLineSummaryList();
    }

    /**
     * 根据机构代码获取机构明细列表
     *
     * 此方法用于获取特定机构代码对应的费用支出明细列表
     * 它允许用户根据机构代码筛选费用信息，以便进行更详细的分析
     *
     * @param companyCode 机构代码，用于筛选费用信息
     * @return 包含特定机构费用明细的列表
     */
    @Override
    public List<ExpenseResponse> getCompanySummaryList(String companyCode) {
        return expenseManager.getCompanySummaryList(companyCode);
    }

    /**
     * 根据人员代码获取人员明细列表
     *
     * 该方法用于获取特定人员代码对应的绩效明细列表
     * 它使用户能够根据人员代码过滤绩效信息，以便进行个人绩效的分析
     *
     * @param personnelCode 人员代码，用于过滤绩效信息
     * @return 包含特定人员绩效明细的列表
     */
    @Override
    public List<ExpenseResponse> getPersonnelCodeSummaryList(String personnelCode) {
        return expenseManager.getPersonnelCodeSummaryList(personnelCode);
    }

    /**
     * 获取单元格的字符串值
     * 此方法根据单元格的类型返回相应的字符串值，如果单元格为空或类型不匹配，则返回空字符串
     *
     * @param cell 单元格对象，代表Excel中的一个单元格
     * @return 单元格的字符串值，如果单元格为空或类型不匹配，则返回空字符串
     */
    private String getCellValue(Cell cell) {
        if (cell == null) {
            return "";
        }
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                return String.valueOf(cell.getNumericCellValue());
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                return cell.getCellFormula();
            default:
                return "";
        }
    }

    /**
     * 获取单元格的数值
     * 此方法主要用于获取单元格中的数值，如果单元格为空或类型不是数值型，则返回0.0
     * 如果单元格类型为字符串型，尝试将其转换为数值，转换失败则返回0.0
     *
     * @param cell 单元格对象，代表Excel中的一个单元格
     * @return 单元格的数值，如果单元格为空或类型不是数值型，则返回0.0
     */
    private double getCellNumericValue(Cell cell) {
        if (cell == null) {
            return 0.0;
        }
        if (cell.getCellType() == CellType.STRING) {
            try {
                return Double.parseDouble(cell.getStringCellValue());
            } catch (NumberFormatException e) {
                return 0.0;
            }
        }
        return cell.getNumericCellValue();
    }
}
